/* *****************************************************************************
 * JFab (http://code.google.com/p/jfab)
 * Copyright (c) 2011 JFab.org
 * Admin jfab@jeffreythompson.net
 *
 * See the file "LICENSE.txt" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 * *****************************************************************************/

package org.jfab.core.factory;

import java.beans.PropertyChangeSupport;
import java.util.Collections;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.Validate;
import org.jfab.core.collection.CGAnnotationList;
import org.jfab.core.collection.CGFieldList;
import org.jfab.core.collection.CGPropertyList;
import org.jfab.core.collection.CGTypeList;
import org.jfab.core.collection.CommentList;
import org.jfab.core.collection.ModifierList;
import org.jfab.core.model.CGField;
import org.jfab.core.model.CGProperty;
import org.jfab.core.model.CGType;
import org.jfab.core.model.Modifier;

/**
 * Provides a field factory.
 */
public final class DefaultCGFieldFactory implements CGFieldFactory
{
    /** String factory. */
    private final StringFactory _stringFactory;

    /** Type factory. */
    private final CGTypeFactory _typeFactory;

    /**
     * Construct this object with the given parameters.
     * 
     * @param stringFactory String factory.
     * @param typeFactory Type factory.
     */
    public DefaultCGFieldFactory(final StringFactory stringFactory,
            final CGTypeFactory typeFactory)
    {
        _stringFactory = stringFactory;
        _typeFactory = typeFactory;
    }

    @Override
    public CGField createDefaultSerialVersionIdField()
    {
        final CGTypeList referencedTypes = null;
        final CommentList commentLines = new CommentList(
                Collections.singletonList("Serial version UID."));
        final CGAnnotationList annotations = null;
        final ModifierList modifiers = new ModifierList();
        modifiers.add(Modifier.PRIVATE.getName());
        modifiers.add(Modifier.STATIC.getName());
        modifiers.add(Modifier.FINAL.getName());
        final CGType fieldType = getTypeFactory().createType(long.class);
        final String fieldName = "serialVersionUID";
        final String initialValue = "1L";
        final Boolean isGetter = null;
        final Boolean isSetter = null;
        final Boolean isUsedInIdentity = Boolean.FALSE;

        return createField(referencedTypes, commentLines, annotations,
                modifiers, fieldType, fieldName, initialValue, isGetter,
                isSetter, isUsedInIdentity);
    }

    @Override
    public CGField createField(final CGProperty property)
    {
        final CGTypeList referencedTypes = new CGTypeList(
                property.getReferencedTypes());
        final CommentList commentLines = new CommentList(
                property.getCommentLines());
        final CGAnnotationList annotations = new CGAnnotationList(
                property.getAnnotations());
        final ModifierList modifiers = new ModifierList(property.getModifiers());
        final CGType fieldType = property.getType();
        final String fieldName = property.getName();
        final String initialValue = property.getInitialValue();
        final Boolean isGetter = property.isGetter();
        final Boolean isSetter = property.isSetter();
        final Boolean isUsedInIdentity = property.isUsedInIdentity();

        return createField(referencedTypes, commentLines, annotations,
                modifiers, fieldType, fieldName, initialValue, isGetter,
                isSetter, isUsedInIdentity);
    }

    @Override
    public final CGField createField(final CGType fieldType,
            final String fieldName)
    {
        final CGTypeList referencedTypes = null;
        final String comment = getStringFactory()
                .createCommentFromPropertyName(fieldName);
        final CommentList commentLines = new CommentList(
                Collections.singletonList(comment));
        final CGAnnotationList annotations = null;
        final ModifierList modifiers = new ModifierList(Modifier.PRIVATE);
        final String initialValue = null;
        final Boolean isGetter = null;
        final Boolean isSetter = null;
        final Boolean isUsedInIdentity = Boolean.TRUE;

        return createField(referencedTypes, commentLines, annotations,
                modifiers, fieldType, fieldName, initialValue, isGetter,
                isSetter, isUsedInIdentity);
    }

    @Override
    public final CGField createField(final CGType fieldType,
            final String fieldName, final String initialValue)
    {
        final CGTypeList referencedTypes = null;
        final String comment = getStringFactory()
                .createCommentFromPropertyName(fieldName);
        final CommentList commentLines = new CommentList(
                Collections.singletonList(comment));
        final CGAnnotationList annotations = null;
        final ModifierList modifiers = new ModifierList(Modifier.PRIVATE);
        final Boolean isGetter = null;
        final Boolean isSetter = null;
        final Boolean isUsedInIdentity = Boolean.TRUE;

        return createField(referencedTypes, commentLines, annotations,
                modifiers, fieldType, fieldName, initialValue, isGetter,
                isSetter, isUsedInIdentity);
    }

    @Override
    public final CGField createField(final CGTypeList referencedTypes,
            final CommentList commentLines, final CGAnnotationList annotations,
            final ModifierList modifiers, final CGType fieldType,
            final String fieldName, final String initialValue,
            final Boolean isGetter, final Boolean isSetter,
            final Boolean isUsedInIdentity)
    {
        Validate.notEmpty(fieldName, "fieldName is empty");

        final CGField answer = new CGField();

        if (referencedTypes != null)
        {
            answer.getReferencedTypes().addAll(referencedTypes);
        }

        if (commentLines != null)
        {
            answer.getCommentLines().addAll(commentLines);
        }

        if (annotations != null)
        {
            answer.getAnnotations().addAll(annotations);
        }

        if (modifiers != null)
        {
            answer.getModifiers().addAll(modifiers);
        }

        if (fieldType != null)
        {
            answer.setType(fieldType);
        }

        answer.setName(fieldName);

        if (initialValue != null)
        {
            answer.setInitialValue(initialValue);
        }

        if (isGetter != null)
        {
            answer.setGetter(isGetter);
        }

        if (isSetter != null)
        {
            answer.setSetter(isSetter);
        }

        if (isUsedInIdentity != null)
        {
            answer.setUsedInIdentity(isUsedInIdentity);
        }

        return answer;
    }

    @Override
    public final CGField createInstanceFactoryField()
    {
        final CGTypeList referencedTypes = null;
        final CommentList commentLines = new CommentList(
                Collections.singletonList("Instance factory."));
        final CGAnnotationList annotations = null;
        final ModifierList modifiers = new ModifierList(Modifier.PRIVATE);
        final CGType fieldType = getTypeFactory().createType(
                DefaultInstanceFactory.class);
        final String fieldName = getStringFactory().getMemberVariablePrefix()
                + "instanceFactory";
        final String initialValue = null;
        final Boolean isGetter = null;
        final Boolean isSetter = null;
        final Boolean isUsedInIdentity = Boolean.FALSE;

        return createField(referencedTypes, commentLines, annotations,
                modifiers, fieldType, fieldName, initialValue, isGetter,
                isSetter, isUsedInIdentity);
    }

    @Override
    public final CGFieldList createPropertyChangeConstantFields(
            final CGFieldList fields)
    {
        final CGFieldList answer = new CGFieldList();

        if (CollectionUtils.isNotEmpty(fields))
        {
            final CGTypeList referencedTypes = null;
            final CGAnnotationList annotations = null;
            final ModifierList modifiers = new ModifierList();
            modifiers.add(Modifier.PUBLIC.getName());
            modifiers.add(Modifier.STATIC.getName());
            modifiers.add(Modifier.FINAL.getName());
            final CGType fieldType = getTypeFactory().createType(String.class);

            for (final CGField field : fields)
            {
                final String propertyName = field.getName();
                final String commentName = getStringFactory()
                        .convertFirstCharacterToUpperCase(propertyName);
                final CommentList commentLines = new CommentList(
                        Collections.singletonList(commentName
                                + " property change name."));
                final String constantName = getStringFactory()
                        .createPropertyChangeName(propertyName);
                final String constantValue = "\"" + propertyName + "\"";
                final Boolean isGetter = field.isGetter();
                final Boolean isSetter = field.isSetter();
                final Boolean isUsedInIdentity = Boolean.FALSE;

                final CGField newField = createField(referencedTypes,
                        commentLines, annotations, modifiers, fieldType,
                        constantName, constantValue, isGetter, isSetter,
                        isUsedInIdentity);
                answer.add(newField);
            }
        }

        return answer;
    }

    @Override
    public CGFieldList createPropertyChangeConstantFields(
            final CGPropertyList properties)
    {
        final CGFieldList answer = new CGFieldList();

        if (CollectionUtils.isNotEmpty(properties))
        {
            final CGTypeList referencedTypes = null;
            final CGAnnotationList annotations = null;
            final ModifierList modifiers = new ModifierList();
            modifiers.add(Modifier.PUBLIC.getName());
            modifiers.add(Modifier.STATIC.getName());
            modifiers.add(Modifier.FINAL.getName());
            final CGType fieldType = getTypeFactory().createType(String.class);

            for (final CGProperty property : properties)
            {
                final String propertyName = property.getName();
                final String commentName = getStringFactory()
                        .convertFirstCharacterToUpperCase(propertyName);
                final CommentList commentLines = new CommentList(
                        Collections.singletonList(commentName
                                + " property change name."));
                final String constantName = getStringFactory()
                        .createPropertyChangeName(propertyName);
                final String constantValue = "\"" + propertyName + "\"";
                final Boolean isGetter = Boolean.FALSE;
                final Boolean isSetter = Boolean.FALSE;
                final Boolean isUsedInIdentity = Boolean.FALSE;

                final CGField newField = createField(referencedTypes,
                        commentLines, annotations, modifiers, fieldType,
                        constantName, constantValue, isGetter, isSetter,
                        isUsedInIdentity);
                answer.add(newField);
            }
        }

        return answer;
    }

    @Override
    public final CGField createPropertyChangeSupportField()
    {
        final CGTypeList referencedTypes = null;
        final CommentList commentLines = new CommentList(
                Collections.singletonList("Property change support."));
        final CGAnnotationList annotations = null;
        final ModifierList modifiers = new ModifierList();
        modifiers.add(Modifier.PRIVATE.getName());
        modifiers.add(Modifier.TRANSIENT.getName());
        final CGType fieldType = getTypeFactory().createType(
                PropertyChangeSupport.class);
        final String fieldName = getStringFactory().createMemberVariableName(
                "propertyChangeSupport");
        final Boolean isGetter = null;
        final Boolean isSetter = null;
        final Boolean isUsedInIdentity = Boolean.FALSE;

        return createField(referencedTypes, commentLines, annotations,
                modifiers, fieldType, fieldName, null, isGetter, isSetter,
                isUsedInIdentity);
    }

    @Override
    public final CGField createPropertyField(final CGField property)
    {
        final String fieldName = getStringFactory().createMemberVariableName(
                property.getName());
        final CGTypeList referencedTypes = new CGTypeList(
                property.getReferencedTypes());
        final CommentList commentLines = new CommentList(
                property.getCommentLines());
        final CGAnnotationList annotations = new CGAnnotationList(
                property.getAnnotations());
        final ModifierList modifiers = new ModifierList(property.getModifiers());
        final Boolean isGetter = property.isGetter();
        final Boolean isSetter = property.isSetter();
        final Boolean isUsedInIdentity = property.isUsedInIdentity();

        return createField(referencedTypes, commentLines, annotations,
                modifiers, property.getType(), fieldName,
                property.getInitialValue(), isGetter, isSetter,
                isUsedInIdentity);
    }

    @Override
    public final CGFieldList createPropertyFields(final CGFieldList properties)
    {
        final CGFieldList answer = new CGFieldList();

        if (CollectionUtils.isNotEmpty(properties))
        {
            for (final CGField property : properties)
            {
                answer.add(createPropertyField(property));
            }
        }

        return answer;
    }

    /**
     * @return the stringFactory
     */
    private StringFactory getStringFactory()
    {
        return _stringFactory;
    }

    /**
     * @return the typeFactory
     */
    private CGTypeFactory getTypeFactory()
    {
        return _typeFactory;
    }
}
